package com.quectel.pay.module.order.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.base.Preconditions;
import com.quectel.constant.global.SystemConstants;
import com.quectel.util.aspect.lock.annotation.Lock;
import com.quectel.pay.constants.RedisLockConstants;
import com.quectel.pay.ex.HandOrderException;
import com.quectel.pay.module.merchant.service.PayChannelService;
import com.quectel.pay.module.order.constants.OrderConstants;
import com.quectel.pay.module.order.constants.OrderRefundConstants;
import com.quectel.pay.module.order.dao.PayOrderRefundDao;
import com.quectel.pay.module.order.dto.PayOrderDto;
import com.quectel.pay.module.order.dto.PayOrderRefundDto;
import com.quectel.pay.module.order.dto.response.SendRefundResp;
import com.quectel.pay.module.order.entity.PayOrderRefundEntity;
import com.quectel.pay.module.order.service.*;
import com.quectel.util.common.CopyUtils;
import com.quectel.util.common.JacksonUtils;
import com.quectel.util.kit.Snowflake;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author ryan
 * @date 2021-01-27 14:22:03
 */
@DubboService
public class PayOrderRefundServiceImpl implements PayOrderRefundService {

    private static final Logger LOGGER = LoggerFactory.getLogger(PayOrderRefundServiceImpl.class);

    @Autowired
    private OrderWechatAppPayService orderWechatAppPayService;
    @Autowired
    private OrderWechatJSAPIPayService orderWechatJSAPIPayService;
    @Autowired
    private OrderAliAppPayService orderAliAppPayService;
    @Autowired
    private OrderAliJSAPIPayService orderAliJSAPIPayService;

    @Autowired
    private PayOrderRefundDao payOrderRefundDao;
    @Autowired
    private PayOrderService payOrderService;
    @Autowired
    private PayChannelService payChannelService;

    @Override
    public List<PayOrderRefundDto> selectByOrderId(Long orderId) {
        List<PayOrderRefundEntity> payOrderRefundEntities = payOrderRefundDao.selectList(new LambdaQueryWrapper<PayOrderRefundEntity>()
                .eq(PayOrderRefundEntity::getOrderId, orderId));
        List<PayOrderRefundDto> refundDtos = CopyUtils.copyList(payOrderRefundEntities, PayOrderRefundDto.class);
        return refundDtos;
    }

    @Override
    public PayOrderRefundDto selectByOutRefundNo(String outRefundNo) {
        PayOrderRefundEntity entity = payOrderRefundDao.selectOne(new QueryWrapper<PayOrderRefundEntity>().eq("out_refund_no", outRefundNo));
        PayOrderRefundDto payOrderRefundDto = CopyUtils.copyObj(entity, PayOrderRefundDto.class);
        if (payOrderRefundDto != null) {
            payOrderRefundDto.setPayOrderDto(payOrderService.selectById(payOrderRefundDto.getOrderId()));
        }
        return payOrderRefundDto;
    }

    @Override
    @Lock(
            lockExpresses = {
                    RedisLockConstants.ORDER_REFUND_KEY + "#{#outTradeNo}"
            }
    )
    @Transactional
    public void signRefundSuccess(String outTradeNo, String outRefundNo, Date refundSuccessTime) {
        //退款成功
        PayOrderRefundDto orderRefundDto = selectByOutRefundNo(outRefundNo);
        if (orderRefundDto == null) {
            throw new HandOrderException("Refund order not exist");
        }
        //退款成功 针对微信H5
        if (OrderRefundConstants.OrderRefundStatus.REFUNDING.getStatus().equals(orderRefundDto.getRefundStatus())) {
            //只有在退款中的我们才更新
            PayOrderRefundDto updateObj = new PayOrderRefundDto();
            updateObj.setId(orderRefundDto.getId());
            updateObj.setRefundSuccessTime(refundSuccessTime);
            updateObj.setEditTime(new Date());
            updateObj.setRefundStatus(OrderRefundConstants.OrderRefundStatus.REFUND_SUCCESS.getStatus());
            updateById(updateObj);

            //然后标记订单金额
            PayOrderDto orderDto = payOrderService.selectById(orderRefundDto.getOrderId());
            if (orderDto != null) {
                PayOrderDto updateOrderObj = new PayOrderDto();
                updateOrderObj.setId(orderDto.getId());

                BigDecimal totalRefunFee = orderDto.getTotalRefundFee() == null ? new BigDecimal("0") : orderDto.getTotalRefundFee();
                updateOrderObj.setTotalRefundFee(totalRefunFee.add(orderRefundDto.getRefundAmount()));

                payOrderService.updateById(updateOrderObj);
            }
            LOGGER.info("Sign refund order success, outTradeNo: {},outRefundNo: {}", outTradeNo, outRefundNo);
        }
    }

    @Override
    @Lock(
            lockExpresses = {
                    RedisLockConstants.ORDER_REFUND_KEY + "#{#outTradeNo}"
            }
    )
    @Transactional
    public SendRefundResp sendRefundRequest(String outTradeNo, String outRefundNo, BigDecimal refundAmount, Byte refundType, String refundNotifyUrl, String account) {


        Preconditions.checkArgument(StringUtils.isNotEmpty(outTradeNo), "The field outTradeNo must be not null");

        Preconditions.checkArgument(StringUtils.isNotEmpty(outRefundNo), "The field outRefundNo must be not null");

        Preconditions.checkArgument(refundAmount != null
                && refundAmount.compareTo(new BigDecimal("0")) > 0, "Refund amount must more than zero");

        Preconditions.checkArgument(refundType != null, "Refund type must be not null");


        LOGGER.info("Refund start, outTradeNo is :{} refund amount is {} refund notify url is:{}", outTradeNo, refundAmount.toPlainString(), refundNotifyUrl);

        PayOrderDto orderDto = payOrderService.selectByOutTradeNo(outTradeNo);

        LOGGER.debug("Refund order detail info: {}", JacksonUtils.toJsonString(orderDto));

        Preconditions.checkNotNull(orderDto, "Original order not exists");

        Preconditions.checkArgument(
                OrderConstants.Status.PAY_SUCCESS.getStatusValue().equals(orderDto.getStatus()),
                "This order pay status error"
        );

        String realRefundNotifyUrl = refundNotifyUrl != null ? refundNotifyUrl : orderDto.getRefundNotifyUrl();
        //初始化退款订单信息
        PayOrderRefundDto payOrderRefundDto = initAndSaveRefundOrder(orderDto, outRefundNo, refundAmount, refundType, realRefundNotifyUrl, account);


        SendRefundResp sendRefundResp = null;

        if (SystemConstants.PayType.ZFB.getType().equals(orderDto.getPayType())) {
            //支付宝
            sendRefundResp = orderAliJSAPIPayService.sendRefundRequest(orderDto.getPayChannelId(), payOrderRefundDto);
        }

        if (SystemConstants.PayType.WX.getType().equals(orderDto.getPayType())) {
            if (StringUtils.isBlank(realRefundNotifyUrl)) {
                throw new HandOrderException("Refund notify url must be not null with Wechat-Pay");
            }
            //微信
            if (SystemConstants.TerminalType.JSAPI.getType().equals(orderDto.getTerminalType())) {
                //微信jsapi
                sendRefundResp = orderWechatJSAPIPayService.sendRefundRequest(orderDto.getPayChannelId(), payOrderRefundDto);
            }
            if (SystemConstants.TerminalType.APP.getType().equals(orderDto.getTerminalType())) {
                //微信APP
                sendRefundResp = orderWechatAppPayService.sendRefundRequest(orderDto.getPayChannelId(), payOrderRefundDto);
            }
        }
        // 至少需要找到一个退款逻辑 没有找到说明是非法的退款
        Preconditions.checkNotNull(sendRefundResp, "Illegal refund request");

        if (sendRefundResp.isSendSuccess()) {
            // 发送成功
            if (sendRefundResp.isRefundWithSynchronization()) {
                // 如果是同步退款这时需要立马标记退款成功
                signRefundSuccess(payOrderRefundDto.getOutTradeNo(), payOrderRefundDto.getOutRefundNo(), sendRefundResp.getRefundSuccessTime());
            }

        } else {
            // 发送失败，那我们删除刚才产生的无效订单(失败订单无需存入)
            deleteById(payOrderRefundDto.getId());
        }

        return sendRefundResp;

    }


    /**
     * @param orderDto        订单信息
     * @param outRefundNo     退款订单号
     * @param refundAmount    退款金额
     * @param refundType      1自动退款2手动退款
     * @param refundNotifyUrl 回调地址 支付宝不需要
     * @param account         退款账号
     * @return
     */
    private PayOrderRefundDto initAndSaveRefundOrder(PayOrderDto orderDto, String outRefundNo, BigDecimal refundAmount, Byte refundType, String refundNotifyUrl, String account) {
        PayOrderRefundDto orderRefundDto = selectByOutRefundNo(outRefundNo);
        Date date = new Date();
        if (orderRefundDto == null) {
            //存入退款订单号
            orderRefundDto = new PayOrderRefundDto();
            orderRefundDto.setId(Snowflake.nextId());
            orderRefundDto.setTenantId(orderDto.getTenantId());
            orderRefundDto.setVillageId(orderDto.getVillageId());
            orderRefundDto.setOrderId(orderDto.getId());

            orderRefundDto.setOutTradeNo(orderDto.getOutTradeNo());
            orderRefundDto.setTotalAmount(orderDto.getSpecialPrice());
            orderRefundDto.setOutRefundNo(outRefundNo);
            orderRefundDto.setRefundAmount(refundAmount);
            orderRefundDto.setRefundStatus(OrderRefundConstants.OrderRefundStatus.REFUNDING.getStatus());
            // 说明是直接发起那么就是审核通过
            orderRefundDto.setAuditStatus(OrderRefundConstants.RefundAuditStatus.APPROVED.getStatus());
            // 微信需要单独设置url
            if (SystemConstants.PayType.WX.getType().equals(orderDto.getPayType())) {
                orderRefundDto.setRefundNotifyUrl(refundNotifyUrl);
            }
            orderRefundDto.setRefundType(refundType);
            orderRefundDto.setCreator(account);
            orderRefundDto.setCreateTime(date);
            orderRefundDto.setEditor(account);
            orderRefundDto.setEditTime(date);
            Long id = save(orderRefundDto);
            orderRefundDto.setId(id);
        } else {
            if (!OrderRefundConstants.OrderRefundStatus.REFUND_SUCCESS.getStatus().equals(orderRefundDto.getRefundStatus())) {
                PayOrderRefundDto updateObj = new PayOrderRefundDto();
                updateObj.setId(orderRefundDto.getId());
                updateObj.setEditor(account);
                updateObj.setEditTime(date);
                updateObj.setRefundStatus(OrderRefundConstants.OrderRefundStatus.REFUNDING.getStatus());
                updateObj.setAuditStatus(OrderRefundConstants.RefundAuditStatus.APPROVED.getStatus());
                updateById(updateObj);
            }
        }
        return orderRefundDto;
    }


    @Override
    public List<PayOrderRefundDto> queryList(Map<String, Object> params) {
        List<PayOrderRefundEntity> list = payOrderRefundDao.queryList(params);
        return CopyUtils.copyList(list, PayOrderRefundDto.class);
    }

    @Override
    public int queryTotal(Map<String, Object> params) {
        return payOrderRefundDao.queryTotal(params);
    }

    @Override
    public Long save(PayOrderRefundDto dto) {
        PayOrderRefundEntity entity = CopyUtils.copyObj(dto, PayOrderRefundEntity.class);
        entity.setId(Snowflake.nextId());
        payOrderRefundDao.insert(entity);
        return entity.getId();
    }

    @Override
    public void updateById(PayOrderRefundDto dto) {
        PayOrderRefundEntity entity = CopyUtils.copyObj(dto, PayOrderRefundEntity.class);
        payOrderRefundDao.updateById(entity);
    }


    @Override
    public PayOrderRefundDto selectById(Long id) {
        PayOrderRefundEntity entity = payOrderRefundDao.selectById(id);
        return CopyUtils.copyObj(entity, PayOrderRefundDto.class);
    }

    @Override
    public void deleteById(Long id) {
        payOrderRefundDao.deleteById(id);
    }

    @Override
    @Transactional
    public void deleteBatch(Long[] ids) {
        for (Long id : ids) {
            deleteById(id);
        }
    }
}
